#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import uuid
import re

from db2.comm.const import Db2Const
from db2.comm.db2_cmd import get_db2_databases, check_cluster_node_right, \
    check_cluster_node, check_ha_dr_cluster_node, check_ha_dr_cluster_cfg, get_lang_value
from common.logger import Logger
from db2.comm.constant import DB2Type
from db2.comm.param_parse import Param
from db2.comm.util.common_util import Db2CommonUtil

LOGGER = Logger().get_logger(filename="db2.log")


def get_body_error_param(code: int, body_error: int, message: str):
    params = {
        "code": code,
        "bodyErr": body_error,
        "message": message
    }
    return params


class DatabaseInformation:
    # 数据库信息查询
    def __init__(self, pid, param: Param):
        self.pid = pid
        self.context = param
        self.instance = self.context.application
        self.user_name = self.context.app_auth_key
        self.cluster_type = self.context.app_extend_info.get("clusterType")
        self.lang_value = get_lang_value(self.user_name)

    def get_databases(self):
        """获取database列表"""
        LOGGER.info(f"Begin to get database from  pid :{self.pid}")
        param = self._get_databases_for_pm()
        return param

    def _get_databases_for_pm(self):
        result = self._get_databases()
        result_list = []
        ha_dr_database_message = {}
        ha_dr_database_need_cfg = {}
        params = {}
        ret, hosts_node = check_cluster_node_right()
        if not ret:
            error_message = "Select db2 database nodes failed!"
            LOGGER.error(error_message)
            return params
        host_ip = self._parse_hosts_cluster_nodes(hosts_node)
        ret, datebase_nodes = check_cluster_node(self.user_name)
        for database in result:
            resource = {}
            if self.cluster_type == Db2Const.SINGLE:
                ret, ha_dr_database = check_ha_dr_cluster_node(self.user_name, database[0])
                if ret:
                    LOGGER.info("This is  ha_dr databases!")
                    continue
            if self.cluster_type == Db2Const.HADR:
                ret, ha_dr_database = check_ha_dr_cluster_node(self.user_name, database[0])
                if int(ret) != 0:
                    LOGGER.error("This is not ha_dr databases!")
                    continue
                ha_dr_database_message = self._parse_ha_dr_database_list(ha_dr_database)
                ret, ha_dr_database_cfg = check_ha_dr_cluster_cfg(self.user_name, database[0])
                if int(ret) != 0:
                    LOGGER.error("Query cfg failed!")
                    continue
                ha_dr_database_need_cfg = self._parse_ha_dr_database_cfg(ha_dr_database_cfg)
            node = self._parse_nodes_name(datebase_nodes, database[2])
            result_ip = host_ip.get(node, "")
            database_uuid = str(uuid.uuid5(uuid.NAMESPACE_X500, self.instance.get("id") + DB2Type.DB2DATABASE
                                + self.instance.get("name") + database[0]))
            resource["id"] = database_uuid
            resource["type"] = DB2Type.TYPE
            resource["subType"] = DB2Type.DB2DATABASE
            resource["name"] = database[0]
            resource["parentId"] = self.instance.get("id")
            resource["parentName"] = self.instance.get("name")
            extend_info = {"localDatabaseDirectory": database[1], "catalogNumber": database[2], "catalogIp": result_ip}
            extend_info.update(ha_dr_database_message)
            extend_info.update(ha_dr_database_need_cfg)
            resource["extendInfo"] = extend_info
            result_list.append(resource)
        params = {"resourceList": result_list[0:Db2Const.MAX_DATABASE_NUM]}
        LOGGER.info(f"Already to get database,pid: {self.pid}, length: {len(result_list)}")
        return params

    def _get_databases(self):
        result = []
        ret, database_list = get_db2_databases(self.user_name)
        if not ret or ret == "127":
            error_message = "Query databases failed !"
            LOGGER.error(error_message)
            return result
        if len(database_list) == 0:
            error_message = "Get database failed or database is not exist!"
            LOGGER.error(error_message)
            return result
        result = self._parse_database_list(database_list)
        return result

    def _parse_date_base_node(self, result_nodes: str):
        LOGGER.info(f"Begin to parse tablespace, pid: {self.pid}.")
        pattern = r"\s+(\d+)\n"
        result = re.findall(pattern, result_nodes)
        result_node = ','.join(result)
        return result_node

    def _parse_hosts_cluster_nodes(self, hosts_node):
        LOGGER.info(f"Begin to parse cluster_nodes, pid: {self.pid}.")
        pattern = r"(.*[\.|:]\d+)\s+(.*)\s+"
        result = re.findall(pattern, hosts_node)
        host_node = dict(map(lambda x: (x[1], x[0]), result))
        return host_node

    def _parse_nodes_name(self, cluster_nodes, datebase_node):
        LOGGER.info(f"Begin to parse cluster_nodes, pid: {self.pid}.")
        local_node = ""
        cluster = cluster_nodes.split('\n')
        for cluster_node in cluster:
            if cluster_node == '':
                continue
            node = cluster_node.split(' ')
            if node[0] == datebase_node:
                local_node = node[1]
        return local_node

    def _parse_database_list(self, database_list: str):
        LOGGER.info(f"Begin to parse database list, pid: {self.pid}.")
        pattern = r"Database name.*=\s+(\w+)\s+[^#]*?Local database directory.*=\s+(.*)\s+[^#]*?" \
                  r"Catalog database partition number .*=\s+(\d)\s"
        result = re.findall(pattern, database_list)
        if not result:
            pattern = r"数据库名称.*=\s+(\w+)\s+[^#]*?本地数据库目录.*=\s+(.*)\s+[^#]*?" \
                      r"目录数据库分区号 .*=\s+(\d)\s"
            result = re.findall(pattern, database_list)
        LOGGER.info(f"The database list of this instance is: {result}.")
        return result

    def _parse_ha_dr_database_list(self, database_list: str):
        ha_dr_database_list = database_list.split('\n')
        db_dict = dict()
        for ha_dr_database in ha_dr_database_list:
            if re.match(r"\s*HADR_ROLE\s*=", ha_dr_database):
                tmp_key = ha_dr_database.split("=")[0].strip()
                tmp_name = ha_dr_database.split()[-1].strip()
                db_dict[tmp_key] = tmp_name
                continue
            if re.match(r"\s*HADR_STATE\s*=", ha_dr_database):
                tmp_key = ha_dr_database.split("=")[0].strip()
                tmp_name = ha_dr_database.split("=")[-1].strip()
                db_dict[tmp_key] = tmp_name
                continue
        LOGGER.info(f"Parse ha dr database list success, pid: {self.pid}.")
        return db_dict

    def _parse_ha_dr_database_cfg(self, database_list):
        ha_dr_database_cfg = database_list.split('\n')
        db_dict = dict()
        for ha_dr_database_cfg_message in ha_dr_database_cfg:
            if ") =" not in ha_dr_database_cfg_message:
                continue
            tmp_message = ha_dr_database_cfg_message.split('(')[1]
            tmp_key = tmp_message.split(')')[0]
            tmp_name = tmp_message.split()[-1]
            if tmp_key in {"HADR_LOCAL_HOST", "HADR_LOCAL_SVC", "HADR_REMOTE_HOST",
                           "HADR_REMOTE_SVC", "HADR_TARGET_LIST"}:
                if tmp_key == "HADR_LOCAL_HOST":
                    tmp_name = Db2CommonUtil.domain_conversion_ip(tmp_name)
                if tmp_key == "HADR_REMOTE_HOST":
                    tmp_name = Db2CommonUtil.domain_conversion_ip(tmp_name)
                if tmp_key == "HADR_TARGET_LIST":
                    if tmp_name == "=":
                        tmp_name = ""
                db_dict[tmp_key] = tmp_name
        LOGGER.info(f"Parse ha dr database cfg success, pid: {self.pid}.")
        return db_dict
